#include <dspSimulator/instruction/stw.h>

bool STW::checkCondition(std::shared_ptr<DspMachine> dspMachine) {
    if (isCompactInstruction) {
        return true;
    } else {
        return checkStandardCondition(dspMachine);
    }
}

void STW::loadInstIRIntoJit(llvm::LLVMContext *llvmContext, llvm::Module *module,
                            std::shared_ptr<llvm::IRBuilder<>> irBuilder) {
}

std::shared_ptr<Instruction> STW::decode(std::shared_ptr<InstInfo> instInfo, u64 baseCycle) {
    std::shared_ptr<STW> stw;
    auto dspInstInfo = std::dynamic_pointer_cast<DspInstInfo>(instInfo);
    bool isStandardInst = dspInstInfo->isStandardInstruction();
    if (isStandardInst) {
        auto data = vectorToBitSet<32>(dspInstInfo->getBits());
        if (checkBits<5>(data, 2, 0b11101) && data[8] == 0 && existStandardCondition(data)) {
            stw = std::make_shared<STW>(dspInstInfo->getAddress(), baseCycle);
            stw->creg_z = extractBits<4>(data, 28);
            stw->src1 = extractBits<5>(data, 23).to_ulong();
            stw->baseR = extractBits<5>(data, 18).to_ulong();
            stw->offsetR = extractBits<5>(data, 13).to_ulong();
            stw->cst = stw->offsetR;
            stw->mode = extractBits<4>(data, 9).to_ulong();
            stw->y = data[7];
            if (stw->y == 0) {
                stw->funcUnit = FuncUnit::D1;
            } else {
                stw->funcUnit = FuncUnit::D2;
            }
            stw->s = data[1];
            stw->p = data[0];
            stw->instFormat = InstFormat::Kind1;
        }
    } else {
        auto data = vectorToBitSet<16>(dspInstInfo->getBits());
        if (checkBits<7>(data, 0, 0b1110111) && data[11] == 0 && data[15] == 0 && data[14] == 0) {
            // STW (.unit) src,*B15--[ucst2]
            stw = std::make_shared<STW>(dspInstInfo->getAddress(), baseCycle);
            stw->isCompactInstruction = true;
            stw->compactInstKind = "Dpp";
            // 基址寄存器选择B15
            stw->baseR = 15;
            stw->y = 1;
            // offset
            stw->cst = data[13];
            stw->cst += 1;
            // if t(s) == 1,src为BReg,否则src为AReg
            stw->s = data[12];
            stw->src1 = stw->dst = extractBits<4>(data, 7).to_ulong();
            stw->funcUnit = FuncUnit::D2;
            // *B15--[ucst]
            stw->mode = 0b1010;
            stw->PROT = dspInstInfo->getPROT();
            stw->BR = dspInstInfo->getBR();
            stw->DSZ = dspInstInfo->getDSZ();
            stw->SAT = dspInstInfo->getSAT();
            stw->RS = false;
            stw->p = dspInstInfo->getP();
            stw->fixUpRegOnCompactInstruction();
            stw->instFormat = InstFormat::Kind1;
        } else if (checkBits<3>(data, 0, 0b101) && checkBits<2>(data, 10, 0b11) && data[15] == 1 && data[3] == 0) {
            stw = std::make_shared<STW>(dspInstInfo->getAddress(), baseCycle);
            stw->isCompactInstruction = true;
            stw->compactInstKind = "Dstk";
            stw->cst = extractBits<2>(data, 13).to_ulong();
            stw->cst += extractBits<3>(data, 7).to_ulong() << 2;
            stw->s = data[12]; // s is equal to t
            stw->src1 = stw->dst = extractBits<3>(data, 4).to_ulong();
            stw->y = 1;        // use B15 as baseR
            stw->funcUnit = FuncUnit::D2;
            stw->PROT = dspInstInfo->getPROT();
            stw->BR = dspInstInfo->getBR();
            stw->DSZ = dspInstInfo->getDSZ();
            stw->SAT = dspInstInfo->getSAT();
            stw->RS = false;
            stw->p = dspInstInfo->getP();
            stw->fixUpRegOnCompactInstruction();
            stw->instFormat = InstFormat::Kind2;
        } else if (dspInstInfo->getDSZ()[2] == 0 && data[9] == 0 && data[3] == 0 && data[10] == 0 &&
                   checkBits<2>(data, 1, 0b10)) {
            stw = std::make_shared<STW>(dspInstInfo->getAddress(), baseCycle);
            stw->isCompactInstruction = true;
            stw->compactInstKind = "Doff4";
            stw->cst = extractBits<3>(data, 13).to_ulong();
            stw->cst = extractBits<1>(data, 11).to_ulong() << 3;
            stw->src1 = stw->dst = extractBits<3>(data, 4).to_ulong();
            stw->s = data[12]; // s is equal to t
            stw->y = data[0];
            stw->baseR = extractBits<2>(data, 7).to_ulong() + 4;
            if (stw->y == 0) {
                stw->funcUnit = FuncUnit::D1;
            } else {
                stw->funcUnit = FuncUnit::D2;
            }
            stw->mode = 0b0001;
            stw->PROT = dspInstInfo->getPROT();
            stw->BR = dspInstInfo->getBR();
            stw->DSZ = dspInstInfo->getDSZ();
            stw->SAT = dspInstInfo->getSAT();
            stw->RS = false;
            stw->p = dspInstInfo->getP();
            stw->fixUpRegOnCompactInstruction();
            stw->instFormat = InstFormat::Kind1;
        }
    }
    return stw;
}

void STW::executeCustom(std::shared_ptr<TargetMachine> targetMachine, ExecutePhase executePhase) {
    auto dspMachine = std::dynamic_pointer_cast<DspMachine>(targetMachine);
    switch (executePhase) {
        case ExecutePhase::E1:
            if (instFormat == InstFormat::Kind1) {
                u32 *baseRReadPtr;
                u32 *baseRWritePtr;
                if (y == 0) {
                    baseRReadPtr = dspMachine->getSaveRegs()->getReg32ByName(RegAName[baseR])->getDataPtr();
                    baseRWritePtr = dspMachine->getRegisters()->getReg32ByName(RegAName[baseR])->getDataPtr();
                } else {
                    baseRReadPtr = dspMachine->getSaveRegs()->getReg32ByName(RegBName[baseR])->getDataPtr();
                    baseRWritePtr = dspMachine->getRegisters()->getReg32ByName(RegBName[baseR])->getDataPtr();
                }
                m_visitMemAddr = computeRealAddress(baseRReadPtr, baseRWritePtr, mode, 2, dspMachine);
            } else if (instFormat == InstFormat::Kind2) {
                u32 *baseRPtr;
                if (y == 0) {
                    baseRPtr = dspMachine->getSaveRegs()->getReg32ByName("B14")->getDataPtr();
                } else
                    baseRPtr = dspMachine->getSaveRegs()->getReg32ByName("B15")->getDataPtr();
                m_visitMemAddr = *baseRPtr + (cst << 2);
            }
            m_data = *src1LoRegPtr;
            break;
        case ExecutePhase::E3:
            targetMachine->getMMU()->getMemory()->writeNBits<32>(m_visitMemAddr, {m_data});
            break;
        default:
            break;
    }
}

std::shared_ptr<DspInstruction> STW::clone() {
    return std::make_shared<STW>(*this);
}

std::string STW::toString() const {
    std::string res;
    if (!isCompactInstruction) {
        res += getStandardConditionString() + " ";
    }
    res += m_name + " " + FuncUnitName[static_cast<u32>(funcUnit)];
    switch (instFormat) {
        case InstFormat::Kind1:
            res += " " + getSrc1Reg32Name() + "," + addressModeToString();
            break;
        case InstFormat::Kind2:
            res += " " + getSrc1Reg32Name() + ",*+";
            if (y == 0)
                res += "B14[" + std::to_string(cst) + "]";
            else
                res += "B15[" + std::to_string(cst) + "]";
            break;
        default:
            break;
    }
    return res;
}
